package cn.nexd.location.thread;

import android.util.Log;

import org.json.JSONObject;

import java.io.FileInputStream;
import java.io.IOException;

import cn.nexd.location.LocationResult;
import cn.nexd.location.NexdLocation;
import cn.nexd.location.NexdLocationListener;
import cn.nexd.location.core.NexdIndoorLocationAlgorithm;
import cn.nexd.location.utils.Command;
import cn.nexd.location.utils.threadpool.ThreadTaskObject;
import cn.nexd.location.utils.threadpool.core.ThreadPoolDelegate;

/**
 * Created by codngfish on 15/11/30.
 */
public class NexdIndoorLocationThread {

    private NexdLocationListener locationListener;
    private static boolean stop = false;
    private LocationThread locationThread;
    private String collectorResults;
    private boolean initSuccess = false;
    private boolean isFirst = true;
    private ConfigThread configThread;


    public NexdIndoorLocationThread(final NexdLocationListener locationListener) {
        this.locationListener = locationListener;
        isFirst = true;
    }

    //手动切层
    public void start(long taskID, long buildingId, long floorId, String collectorResults) {
        stop = false;
        this.collectorResults = collectorResults;
        locationThread = new LocationThread(taskID, buildingId, floorId);
        ThreadPoolDelegate.getThreadPoolDelegate().getThreadPoolManager().addTask(locationThread);
        Log.d("location_test", "===========" + collectorResults);
    }

    //初始化配置
    public void iniConfig(long buildingId, long floorId) {
        isFirst = true;
        if (buildingId != NexdIndoorLocationAlgorithm.getInstance().getCurrentBuildingId()) {
            NexdIndoorLocationAlgorithm.getInstance().freeLocationEngineForCurrentBuildingId();
        }

        if (floorId == -1L) {
            NexdIndoorLocationAlgorithm.getInstance().freeLocationEngineForCurrentFloorId();
        }

        if (configThread == null) {
            configThread = new ConfigThread(buildingId, floorId);
        }

        if (!configThread.isRunning()) {
            ThreadPoolDelegate.getThreadPoolDelegate().getThreadPoolManager().addTask(configThread);
        }
    }

    public void stopThread() {
        stop = true;
        if (locationThread != null) {
            ThreadPoolDelegate.getThreadPoolDelegate().getThreadPoolManager().removeTask(locationThread);
            locationThread = null;
        }
    }


    private void initLocationConfig(long buildingId, long floorId) throws IOException {

        NexdIndoorLocationAlgorithm.getInstance().setCurrentFloorId(floorId);
        String optparam = Command.getLocationConfigPath(buildingId, floorId) + ".optparam";
        String wp = Command.getLocationConfigPath(buildingId, floorId) + ".wp";
        String wifilist = Command.getLocationConfigPath(buildingId, floorId) + ".wifilist";
        NexdIndoorLocationAlgorithm.getInstance().initIndoorLocationConfig(optparam, wp, wifilist);
    }

    private void initFloorDetectionConfig(long buildingId) throws IOException {
        NexdIndoorLocationAlgorithm.getInstance().setCurrentBuildingId(buildingId);
        //        FileInputStream mean = new FileInputStream(Command.getFloorDetectionConfigPath(buildingId) + ".mean");
        //        FileInputStream var = new FileInputStream(Command.getFloorDetectionConfigPath(buildingId) + ".var");
        //        FileInputStream weight = new FileInputStream(Command.getFloorDetectionConfigPath(buildingId) + ".weight");
        //        FileInputStream wifilist = new FileInputStream(Command.getFloorDetectionConfigPath(buildingId) + ".wifilist");
        //        NexdIndoorLocationAlgorithm.getInstance().initIndoorFloorDetectionConfig(mean, var, weight, wifilist);

        FileInputStream readme = new FileInputStream(Command.getFloorDetectionConfigPath(buildingId) + ".CONF");
        FileInputStream wifilist = new FileInputStream(Command.getFloorDetectionConfigPath(buildingId) + ".wifilist");
        FileInputStream model = new FileInputStream(Command.getFloorDetectionConfigPath(buildingId) + ".model");
        NexdIndoorLocationAlgorithm.getInstance().initIndoorFloorDetectionConfig(readme, model, wifilist);
    }

    private class ConfigThread extends ThreadTaskObject {

        private long buildingId;
        private long floorId;
        private boolean isRunning;

        public ConfigThread(long buildingId, long floorId) {
            this.buildingId = buildingId;
            this.floorId = floorId;
            this.isRunning = false;
        }

        @Override
        public void run() {
            initSuccess = false;
            isRunning = true;
            // 如果是手动切层,那么 floorId !=-1L,这时候并不需要加载切层配置

            if (floorId == -1L) {
                // 需要自动定位,这时候需要首先加载切层文件,然后执行切层定位,然后得到定位 floorId,然后加载对应的配置文件

                //判断已经加载的切层配置是不是需要替换
                if (NexdIndoorLocationAlgorithm.getInstance().getCurrentBuildingId() != buildingId) {
                    //需要加载切层配置文件
                    try {
                        initFloorDetectionConfig(buildingId);
                        initSuccess = true;
                        isRunning = false;
                        configThread = null;
                        return;
                    } catch (IOException e) {
                        Log.e(e.getClass().getSimpleName(), e.getLocalizedMessage());
                        initSuccess = false;
                        stop = true;
                        isRunning = false;

                        configThread = null;
                        NexdIndoorLocationAlgorithm.getInstance().freeLocationEngineForCurrentBuildingId();
                        if (locationListener != null) {
                            locationListener.onLocationInterrupt(NexdLocation.LOCATION_INTERRUPT_TYPE_FLOOR_DETECTION_FILE_NULL, NexdLocation.LOCATION_INTERRUPT_MESSAGE_FLOOR_DETECTION_FILE_NULL);
                            initSuccess = false;
                        }
                        return;
                    }
                }

                //这时候可能没有传入 wifi 的数据,也就是说并不能完成一次切层定位,需要等待wifi 数据传入之后在执行切层定位
            } else {
                //这时候已经明确指定需要在哪个 floorID 执行定位,所以可以直接忽略切层配置文件
                if (NexdIndoorLocationAlgorithm.getInstance().getCurrentFloorId() != floorId) {
                    //配置不对,需要更改
                    try {
                        initLocationConfig(buildingId, floorId);
                        initSuccess = true;
                        isRunning = false;
                        configThread = null;
                        return;
                    } catch (IOException e) {
                        Log.e(e.getClass().getSimpleName(), e.getLocalizedMessage());
                        stop = true;
                        initSuccess = false;
                        isRunning = false;
                        configThread = null;
                        NexdIndoorLocationAlgorithm.getInstance().freeLocationEngineForCurrentFloorId();
                        if (locationListener != null) {
                            locationListener.onLocationInterrupt(NexdLocation.LOCATION_INTERRUPT_TYPE_WIFI_LOCATION_FILE_NULL, NexdLocation.LOCATION_INTERRUPT_MESSAGE_WIFI_LOCATION_FILE_NULL);
                            initSuccess = false;
                        }
                        return;
                    }
                }
            }
            initSuccess = true;
            isRunning = false;
            configThread = null;
        }

        public boolean isRunning() {
            return isRunning;
        }
    }

    private class LocationThread extends ThreadTaskObject {

        private JSONObject locationResult;
        private long taskId;
        private long buildingId;
        private long floorId;

        public LocationThread(long taskId, long buildingId, long floorId) {
            this.taskId = taskId;
            this.buildingId = buildingId;
            this.floorId = floorId;
        }

        @Override
        public void run() {
            //初始化配置文件
            if (!initSuccess || stop) {
                return;
            }

            if (floorId == -1L) {
                //此时需要先计算出切层结果
                long floorIdAuto = Long.parseLong(NexdIndoorLocationAlgorithm.getInstance().locationFloorDetection(collectorResults));
                //判断已经加载的配置文件是不是跟计算结果一致
                floorId = floorIdAuto;
                if (floorIdAuto != NexdIndoorLocationAlgorithm.getInstance().getCurrentFloorId()) {
                    Log.d("location_test", "需要加载配置文件,计算出的切层结果为 == " + floorIdAuto);
                    //加载配置文件
                    try {
                        initLocationConfig(buildingId, floorIdAuto);
                        Log.d("location_test", "加载配置文件");
                    } catch (IOException e) {
                        Log.e(e.getClass().getSimpleName(), e.getLocalizedMessage());
                        NexdIndoorLocationAlgorithm.getInstance().freeLocationEngineForCurrentFloorId();
                        if (locationListener != null) {
                            locationListener.onLocationInterrupt(NexdLocation.LOCATION_INTERRUPT_TYPE_WIFI_LOCATION_FILE_NULL, NexdLocation.LOCATION_INTERRUPT_MESSAGE_WIFI_LOCATION_FILE_NULL);
                            initSuccess = false;
                        }
                        return;
                    }
                }
            }

            // 加载成功,这时候可以定位了

            if (locationListener != null && isFirst) {
                Log.d("location_test", "开始定位");
                isFirst = false;
                locationListener.onLocationStart();
            }


            //得到定位结果
            locationResult = NexdIndoorLocationAlgorithm.getInstance().locationIndoorPosition(collectorResults, buildingId, floorId);
            Log.d("location_test", "定位结果 == " + locationResult.toString());
            if (locationResult != null) {

                long floorId = locationResult.optLong("floor_id", -1L);
                float locationResultX = (float) locationResult.optDouble("x", -1);
                float locationResultY = (float) locationResult.optDouble("y", -1);
                long locationResultBuildingId = locationResult.optLong("building_id", -1L);
                if ((floorId == -1L || locationResultX == -1L || locationResultY == -1L) && locationListener != null) {
                    // 定位失败
                    locationListener.onLocationFailed(NexdLocation.LOCATION_FAILED_TYPE_RESULT_NULL, NexdLocation.LOCATION_FAILED_MESSAGE_RESULT_NULL);
                    locationThread = null;
                } else if (locationListener != null) {
                    locationListener.onLocationSuccess(taskId, new LocationResult(locationResultX, locationResultY, locationResultBuildingId, floorId));
                    locationThread = null;
                }
            } else {
                // 定位失败
                if (locationListener != null) {
                    locationListener.onLocationFailed(NexdLocation.LOCATION_FAILED_TYPE_RESULT_NULL, NexdLocation.LOCATION_FAILED_MESSAGE_RESULT_NULL);
                    locationThread = null;
                }
            }
        }
    }
}
